/*
EP3D is a real-time 3D planet engine , which in addition to providing 
substandard scene rendering and scene management, of course, it also 
provides some basic class libraries to build the entire virtual 
planet, or even the entire universe.

Copyright (C) 2010  Hongjiang Zhang	(zhjwyat@gmail.com)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef  EP3D_MATH_H
#define  EP3D_MATH_H
#include "EP3DBase.h"
#include "EP3DMemory.h"

namespace EP3D
{
	#define PI							3.1415927f			
	#define HALF_PI						1.5707963f			
	#define TWO_PI						6.2831853f			
	#define INV_PI						0.31830989f			
	#define INV_TWO_PI					0.159155f			
	#define INV_HALF_PI					0.636618f

	#define LOGHALF						-0.693147f			// log(0.5)
	#define LOGHALFI					-1.442695f			// Inverse of log(0.5)
	#define DELTA						1e-6f				// Small number for comparing floating point numbers
	#define MAX_DIMENSIONS				4					// Maximum number of dimensions in a noise object
	#define MAX_OCTAVES					128					// Maximum # of octaves in an fBm object
	#define HALF_RAND					(RAND_MAX/2)
	#define MAX_INT						0x7fffffff
	#define MAX_UINT					0xffffffff 
	#define MAX_BYTE					0xff
	#define INDEX_NONE					-1

	#define ARRAY_COUNT(array) \
		(sizeof(array) / sizeof(array[0]))

	#define STRUCT_OFFSET(struc, member) \
		((int)&((struc*)NULL)->member)

	template<class T>
	class Math
	{
	public:
		static T Abs(T a)					{ return (a < 0 ? -a : a); }
		static T Min(T a, T b)				{ return (a < b ? a : b); }
		static T Max(T a, T b)				{ return (a > b ? a : b); }
		static T Avg(T a, T b)				{ return (a + b) / (T)2; }
		static T Clamp(T a, T b, T x)		{ return (x < a ? a : (x > b ? b : x)); }
		static T Lerp(T a, T b, T x)		{ return (a + x * (b - a)); }
		static T Cubic(T a)					{ return a * a * (3 - 2*a); }
		static T Sign(T a)					{ return a < 0 ? (T)-1 : (T)1; }
		static T Square(T a)				{ return a * a; }
		static T SquareWithSign(T a)		{ return a < 0 ? -(a * a) : (a * a); }
		static T Step(T a, T x)				{ return (T)(x >= a); }
		static T Boxstep(T a, T b, T x)		{ return Clamp(0, 1, (x-a)/(b-a)); }
		static T Pulse(T a, T b, T x)		{ return (T)((x >= a) - (x >= b)); }
		static void Swap(T &a, T &b)		{ T t = a; a = b; b = t; }

		static T ToRadians(T tDegress)		{ return tDegress * 0.01745329f}
		static T ToDegrees(T tRadians)		{ return tRadians * 57.295779f; }
		static T Sin(T a)					{ return (T)sin((f64)a); }
		static T Cos(T a)					{ return (T)cos((f64)a); }
		static T Tan(T a)					{ return (T)tan((f64)a); }
		static T Asin(T a)					{ return (T)asin((f64)a); }
		static T Acos(T a)					{ return (T)acos((f64)a); }
		static T Atan(T a)					{ return (T)atan((f64)a); }
		static T Atan2(T a, T b)			{ return (T)atan2((f64)a, (f64)b); }
		static T Sqrt(T a)					{ return (T)sqrt((f64)a); }
		static T Pow(T a, T b)				{ return (T)pow((f64)a, (f64)b); }
		static T Log(T a)					{ return (T)log((f64)a); }
		static T Log2(T a)					{ return Log(a)/Log((T)2); }
		static T Log10(T a)					{ return Log(a)/Log((T)10); }
		static T Exp(T a)					{ return (T)exp((f64)a); }

		static int Floor(T a)				{ return ((int)a - (a < 0 && a != (int)a)); }
		static int Ceiling(T a)				{ return ((int)a + (a > 0 && a != (int)a)); }
		static T SqrtWithSign(T a)			{ return a < 0? -Sqrt(-a):Sqrt(a); }
		static T Gamma(T a, T g)			{ return Pow(a, (T)1/g); }
		static T Bias(T a, T b)				{ return Pow(a, Log(b) * LOGHALFI); }
		static T Expose(T l, T k)			{ return (T)1.0 - Exp(-l * k); }

		static T DegToRad(T a)				{ return a * PI * (T)0.00555556f; }
		static T RadToDeg(T a)				{ return a * (T)180.0f * INV_PI; }

		static T Gain(T a, T b)
		{
			if(a <= DELTA)
				return 0.0f;
			if(a >= (T)1.0-DELTA)
				return 1.0f;
	
			T p = (Log(1.0f - b) * LOGHALFI);
			if(a < 0.5f)
				return Pow(2 * a, p) * 0.5f;
			else
				return 1.0f - Pow(2 * (1.0f - a), p) * 0.5f;
		}

		static T Smoothstep(T a, T b, T x)
		{
			if(x <= a)
				return 0.0f;
			if(x >= b)
				return 1.0f;
			return Cubic((x - a) / (b - a));
		}

		static T Mod(T a, T b)
		{
			a -= ((int)(a / b)) * b;
			if(a < 0.0f)
				a += b;
			return a;
		}

		static void Normalize(float *f, int n)
		{
			int i;
			float fMagnitude = 0;
			for(i=0; i<n; i++)
				fMagnitude += f[i]*f[i];
			fMagnitude = 1 / sqrtf(fMagnitude);
			for(i=0; i<n; i++)
				f[i] *= fMagnitude;
		}

	};

	template <typename T>
	class FastMath
	{
	public:
		enum {ANGLE_SHIFT 	= 2};		// Bits to right-shift to get lookup value.
		enum {ANGLE_BITS	= 14};		// Number of valid bits in angles.
		enum {NUM_ANGLES 	= 16384}; 	// Number of angles that are in lookup table.
		enum {ANGLE_MASK    =  (((1<<ANGLE_BITS)-1)<<(16-ANGLE_BITS))};

		static void Init()
		{
			if (NULL == ms_pkTrig) {
				ms_pkTrig = EP3DNew f64[NUM_ANGLES];
				for(int i = 0; i < NUM_ANGLES; i++) {
					ms_pkTrig[i] = Mathd::Sin((f64)i * 2.0f * PI / (f64)NUM_ANGLES);
				}
			}
		}

		static void Close()
		{
			if (NULL != ms_pkTrig) {
				EP3DDelete [] ms_pkTrig;
			}
		}

		static T SinTab(int i)
		{
			if (NULL == ms_pkTrig) {
				Init();
			}
			return (T)ms_pkTrig[((i >> ANGLE_SHIFT)&(NUM_ANGLES - 1))];
		}

		static T CosTab(int i)
		{
			if (NULL == ms_pkTrig) {
				Init();
			}
			return (T)ms_pkTrig[(((i + 16384) >> ANGLE_SHIFT)&(NUM_ANGLES - 1))];
		}

		static T Sin(T a)
		{
			if (NULL == ms_pkTrig) {
				Init();
			}
			return SinTab((int)((a * 65536.0f) / (2.0f * PI)));
		}

		static T Cos(T a)
		{
			if (NULL == ms_pkTrig) {
				Init();
			}
			return CosTab((int)((a * 65536.0f) / (2.0f * PI)));
		}

	private:
		static f64* ms_pkTrig;
	};

	template <class T>
	f64* FastMath<T>::ms_pkTrig = NULL;

	typedef Math<f32> Mathf;
	typedef Math<f64> Mathd;
	typedef Math<int> Mathi;
	typedef Math<long> Mathl;

	typedef FastMath<f32> FastMathf;
	typedef FastMath<f64> FastMathd;
	typedef FastMath<int> FastMathi;
	typedef FastMath<long> FastMathl;

}




#endif